import java.time.Clock;
import java.util.ArrayList;
import java.util.Date;

public final class Time {
	public static int encodeDayStamp(int y, int m, int d) {
		y -= (m - 3) >>> 31; // if (m <= 2) y--;
		final int era = y / 400; // (y >= 0 ? y : y - 399) / 400;
		final int yoe = y - era * 400; // [0, 399]
		final int doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1; // [0, 365]
		final int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
		return era * 146097 + doe - 719468;
	}

	public static int decodeDaystamp(int daystamp) {
		daystamp += 719468;
		final int era = daystamp / 146097; // (daystamp >= 0 ? daystamp : daystamp - 146096) / 146097;
		final int doe = daystamp - era * 146097; // [0, 146096]
		final int yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
		final int doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
		final int mp = (5 * doy + 2) / 153; // [0, 11]
		final int d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
		final int m = mp < 10 ? mp + 3 : mp - 9; // [1, 12]
		final int y = yoe + era * 400 + ((m - 3) >>> 31); // if (m <= 2) y++;
		return (y << 9) + (m << 5) + d;
	}

	public static void checkTimeApi() {
		final long t = System.currentTimeMillis();
		long tt = t;
		ArrayList<Long> list = new ArrayList<>();
		for (int i = 0; i < 10_000_000; i++) {
			final long v = System.currentTimeMillis();
			if (tt != v) {
				list.add((v - t) * 100_000_000 + i);
				tt = v;
			}
			if (v - t > 20)
				break;
		}
		list.forEach(System.out::println);
	}

	public static void timeApiBenchmark() {
		long v = 0, t;

		final Clock clock = Clock.systemUTC();
		t = System.nanoTime();
		for (int i = 0; i < 1_000_000; i++)
			v += clock.instant().getNano();
		System.out.println(System.nanoTime() - t + ", " + v);

		t = System.nanoTime();
		for (int i = 0; i < 1_000_000; i++)
			v += new Date().getTime();
		System.out.println(System.nanoTime() - t + ", " + v);

		t = System.nanoTime();
		for (int i = 0; i < 1_000_000; i++)
			v += System.nanoTime();
		System.out.println(System.nanoTime() - t + ", " + v);

		t = System.nanoTime();
		for (int i = 0; i < 1_000_000; i++)
			v += System.currentTimeMillis();
		System.out.println(System.nanoTime() - t + ", " + v);
	}

	public static void testDaystampCodec() {
		final long t = System.currentTimeMillis();
		final long sec = t / 1000;
		final long date = sec / 86400;
//		long year = date / (365 * 4 + 1) * 4;
//		long y4r = date % (365 * 4 + 1);
//		long yday;
//		boolean ryear;
//		if (y4r >= 365 + 365 + 366) {
//			year += 3;
//			yday = y4r - (365 + 365 + 366);
//			ryear = false;
//		} else if (y4r >= 365 + 365) {
//			year += 2;
//			yday = y4r - (365 + 365);
//			ryear = true;
//		} else if (y4r >= 365) {
//			year += 1;
//			yday = y4r - 365;
//			ryear = false;
//		} else {
//			yday = y4r;
//			ryear = false;
//		}
//		System.out.println(1970 + year);
//		System.out.println(yday);
		final int ymd = decodeDaystamp((int)date);
		final int y = ymd >> 9;
		final int m = (ymd >> 5) & 0xf;
		final int d = ymd & 0x1f;
		System.out.println(y + "-" + m + '-' + d);
		final int date2 = encodeDayStamp(y, m, d);
		System.out.println(date2);
		System.out.println(date == date2);
	}

	public static void main(String[] args) {
//		checkTimeApi();
//		timeApiBenchmark();
		testDaystampCodec();
	}
}
